#if !defined(xtd_obj_fun_h)
#define xtd_obj_fun_h

#include <functional>

namespace xtd {

//////////////////
// no arguments //
//////////////////

template <class T, class R>
class obj_fun_t {
public:
	obj_fun_t(T &obj, R (T::*fun)()) :
		_obj(obj), _fun(fun) {}
	R operator()() const
		{ return (_obj.*_fun)(); }
private:
	T &_obj;
	R (T::*_fun)();
};

template <class T, class R>
inline obj_fun_t<T,R> obj_fun(T &obj, R (T::*fun)())
	{ return obj_fun_t<T,R>(obj, fun); }

// constant function

template <class T, class R>
class obj_func_t {
public:
	obj_func_t(T const & obj, R (T::*fun)() const) :
		_obj(obj), _fun(fun) {}
	R operator()() const
		{ return (_obj.*_fun)(); }
private:
	T const & _obj;
	R (T::*_fun)() const;
};

template <class T, class R>
inline obj_func_t<T,R> obj_fun(T const & obj, R (T::*fun)() const)
	{ return obj_func_t<T,R>(obj, fun); }

////////////////
// 1 argument //
////////////////

template <class T, class R, class A>
class obj_fun1_t : public std::unary_function<A, R> {
public:
	obj_fun1_t(T &obj, R (T::*fun)(A)) :
		_obj(obj), _fun(fun) {}
	R operator()(A a) const
		{ return (_obj.*_fun)(a); }
private:
	T &_obj;
	R (T::*_fun)(A);
};

template <class T, class R, class A>
inline obj_fun1_t<T,R,A> obj_fun(T &obj, R (T::*fun)(A))
	{ return obj_fun1_t<T,R,A>(obj, fun); }

// constant function

template <class T, class R, class A>
class obj_fun1c_t : public std::unary_function<A, R> {
public:
	obj_fun1c_t(T const & obj, R (T::*fun)(A) const) :
		_obj(obj), _fun(fun) {}
	R operator()(A a) const
		{ return (_obj.*_fun)(a); }
private:
	T const & _obj;
	R (T::*_fun)(A) const;
};

template <class T, class R, class A>
inline obj_fun1c_t<T,R,A> obj_fun(T &obj, R (T::*fun)(A) const)
	{ return obj_fun1c_t<T,R,A>(obj, fun); }

/////////////////
// 2 arguments //
/////////////////

template <class T, class R, class A1, class A2>
class obj_fun2_t : public std::binary_function<A1, A2, R> {
public:
	obj_fun2_t(T &obj, R (T::*fun)(A1, A2)) :
		_obj(obj), _fun(fun) {}
	R operator()(A1 a1, A2 a2) const
		{ return (_obj.*_fun)(a1, a2); }
private:
	T &_obj;
	R (T::*_fun)(A1, A2);
};

template <class T, class R, class A1, class A2>
inline obj_fun2_t<T,R,A1,A2> obj_fun(T &obj, R (T::*fun)(A1, A2))
	{ return obj_fun2_t<T,R,A1,A2>(obj, fun); }

// constant function

template <class T, class R, class A1, class A2>
class obj_fun2c_t : public std::binary_function<A1, A2, R> {
public:
	obj_fun2c_t(T const & obj, R (T::*fun)(A1, A2) const) :
		_obj(obj), _fun(fun) {}
	R operator()(A1 a1, A2 a2) const
		{ return (_obj.*_fun)(a1, a2); }
private:
	T const & _obj;
	R (T::*_fun)(A1, A2) const;
};

template <class T, class R, class A1, class A2>
inline obj_fun2c_t<T,R,A1,A2> obj_fun(T const & obj, R (T::*fun)(A1, A2) const)
	{ return obj_fun2c_t<T,R,A1,A2>(obj, fun); }

} //namespace xtd

#endif // xtd_obj_fun_h
